//! ZigZag 是一种对int, long类型数据的压缩方案，其在很多开源软件中都有应用：如：`Protocol Buffer`,`Thrift`,`Avro`中均有应用。
//! 对有符号无符号都可以进行压缩。
//! i32的没有问题了，U32  i64 u64的还有一点问题。

use std::{ops::{BitAnd, Not, ShrAssign}, usize};

//

pub trait ZigZag {
    fn zz_encode(self, buf: &mut [u8] , pos: usize) -> usize;
    fn zz_decode(&mut self, buf: &[u8], pos: usize) -> usize;
}

impl ZigZag for i32 {
    fn zz_encode(self, buf: &mut [u8], pos: usize) -> usize {
        let mut n = (self << 1) ^ (self >> 31);
        let mut end = pos;
        if n & 0x7f.not() != 0 {
            buf[end] = ((n | 0x80) & 0xFF) as u8;
            end += 1;
            n >>= 7;
            while n > 0x7F {
                buf[end] = ((n | 0x80) & 0xFF) as u8;
                end += 1;
                n >>= 7;
            }
        }

        buf[end] = n as u8;
        end += 1;

        end - pos
    }


    fn zz_decode(&mut self, buf: &[u8], pos: usize) -> usize {
        let mut len:usize = 1;
        let mut b = (buf[pos] & 0xFF) as i32;
        let mut n = b & 0x7F;
        
        while b > 0x7F {
            b = (buf[pos + len] & 0xFF) as i32;
            n ^= ( b & 0x7F) << (7 * len);
            len += 1;
            if len == 5 && b > 0x7f {
                panic!("Invalid int encoding");
            }
        }

        *self = (n >> 1) ^ (- (n & 1 ));

        len
    }
}


impl ZigZag for u32 {
    fn zz_encode(self, buf: &mut [u8], pos: usize) -> usize {
        let mut n = (self << 1) ^ (self >> 31);
        let mut end = pos;
        if n.bitand((0x7F as u32).not()) != 0 {
            buf[end] = ((n | 0x80) & 0xFF) as u8;
            end += 1;
            n.shr_assign(7);
            while n > (0x7F as u32) {
                buf[end] = ((n | 0x80) & 0xFF) as u8;
                end += 1;
                n.shr_assign(7);
            }
        }

        buf[end] = n as u8;
        end += 1;

        end - pos
    }


    fn zz_decode(&mut self, buf: &[u8], pos: usize) -> usize {
        let mut len:usize = 1;
        let mut b = (buf[pos] & 0xFF) as u32;
        let mut n = b & 0x7F;
        
        while b > 0x7F {
            b = (buf[pos + len] & 0xFF) as u32;
            n ^= ( b & 0x7F) << (7 * len);
            len += 1;
            if len == 5 && b > 0x7f {
                panic!("Invalid int encoding");
            }
        }

        *self = (n >> 1) ^ (n & 1 );

        len
    }
}


impl ZigZag for u64 {
    fn zz_encode(self, buf: &mut [u8], pos: usize) -> usize {
        let mut n = (self << 1) ^ (self >> 63);
        let mut end = pos;
        if n.bitand((0x7F as u64).not()) != 0 {
            buf[end] = ((n | 0x80) & 0xFF) as u8;
            end += 1;
            n.shr_assign(7);
            while n > (0x7F as u64) {
                buf[end] = ((n | 0x80) & 0xFF) as u8;
                end += 1;
                n.shr_assign(7);
            }
        }

        buf[end] = n as u8;
        end += 1;

        end - pos
    }


    fn zz_decode(&mut self, buf: &[u8], pos: usize) -> usize {
        let mut len:usize = 1;
        let mut b = (buf[pos] & 0xFF) as u64;
        let mut n = b & 0x7F;
        
        while b > 0x7F {
            b = (buf[pos + len] & 0xFF) as u64;
            n ^= ( b & 0x7F) << (7 * len);
            len += 1;
            if len == 5 && b > 0x7f {
                panic!("Invalid int encoding");
            }
        }

        *self = (n >> 1) ^ (n & 1 );

        len
    }
}


impl ZigZag for i64 {
    fn zz_encode(self, buf: &mut [u8], pos: usize) -> usize {
        let mut n = (self << 1) ^ (self >> 63);
        let mut end = pos;
        if n.bitand((0x7F as i64).not()) != 0 {
            buf[end] = ((n | 0x80) & 0xFF) as u8;
            end += 1;
            n.shr_assign(7);
            while n > (0x7F as i64) {
                buf[end] = ((n | 0x80) & 0xFF) as u8;
                end += 1;
                n.shr_assign(7);
            }
        }

        buf[end] = n as u8;
        end += 1;

        end - pos
    }


    fn zz_decode(&mut self, buf: &[u8], pos: usize) -> usize {
        let mut len:usize = 1;
        let mut b = (buf[pos] & 0xFF) as i64;
        let mut n = b & 0x7F;
        
        while b > 0x7F {
            b = (buf[pos + len] & 0xFF) as i64;
            n ^= ( b & 0x7F) << (7 * len);
            len += 1;
            // if len == 5 && b > 0x7f {
            //     panic!("Invalid int encoding");
            // }
        }

        *self = (n >> 1) ^ (- (n & 1 ));

        len
    }
}



#[cfg(test)]
mod tests {
    use crate::zigzag::ZigZag;

    #[test]
    fn test_shift () {
       let mut b = vec![0;20];
       let n1 = [ i64::MIN, i32::MAX as i64];
       let mut n = 0 as i64;
       for v in n1.iter() {
            let len = (*v).zz_encode(&mut b, 0);
            println!("encode: {} => len: {} buf: {:?}", v, len, b);
            let d = n.zz_decode(&b, 0);
            println!("decode: get : {} {}", d, n);
            // assert_eq!(&n, v);
            b.fill_with(Default::default);
            n = 0;
       }

       let b = [152, 166, 252, 194,126];
       n = 0;
       let l = n.zz_decode(&b, 0);
       println!("{} {}", l, n);
    }
}